/** File: client.c **/
#include <stdlib.h>
#include "unp.h"
#include "client.h"
#include "get_ifi_info_plus.c"
#include <sys/param.h>
#include <sys/socket.h>
#include "unprtt.h"
#include <setjmp.h>

#define RTT_DEBUG

static sigjmp_buf jmpbuf;

static void sig_alrm(int signo){
	siglongjmp(jmpbuf,1);
}

ssize_t send_recv_Message(int fd,clientArgumentsType clientArgs, const SA* servaddr, socklen_t servlen);
void receiveMessage(int sockfd);
// Returns the temporary ip address. The ownership of the returned pointer
// is transferred to the caller.
char* checkLocal(clientArgumentsType clientArgs){
	struct ifi_info *ifi, *ifihead;	
	struct sockaddr *sa;
	struct sockaddr_in cli, serv, mas;
  // Free this memory when done.
	char *tempIP = (char*) malloc(sizeof(char) * INET_ADDRSTRLEN);
	char nMask[INET_ADDRSTRLEN];
	char *clientIP = (char*) malloc(sizeof(char) * INET_ADDRSTRLEN);
	int i=0;

	for(ifihead = ifi = get_ifi_info_plus(AF_INET,1);ifi != NULL; ifi = ifi->ifi_next){
		//printf("%s\n",ifi->ifi_name);
	
		if ((sa = ifi->ifi_ntmaddr) != NULL) {
		//	printf("Network Mask:%s\n", Sock_ntop_host(sa,sizeof(*sa)));
			strcpy(nMask, Sock_ntop_host(sa,sizeof(*sa)));
		}
		
		if ((sa = ifi->ifi_addr) != NULL) {
		//	printf("IP Addr: %s\n", Sock_ntop_host(sa, sizeof(*sa)));
			inet_pton(AF_INET, clientArgs.serverIPAddress, &(serv.sin_addr));
			inet_pton(AF_INET, Sock_ntop_host(sa, sizeof(*sa)), &(cli.sin_addr));
			inet_pton(AF_INET, nMask, &(mas.sin_addr)); 
		//	printf("Checking if IP belongs to the same subnet as Server IP\n");
		//	printf("Server IP:%s\n",clientArgs.serverIPAddress);

		//	printf("Mask:%x\n",mas.sin_addr.s_addr);
		//	printf("Server:%x\n",serv.sin_addr.s_addr);
		//	printf("Client:%x\n",cli.sin_addr.s_addr);
	
			if((mas.sin_addr.s_addr & serv.sin_addr.s_addr) == (mas.sin_addr.s_addr & cli.sin_addr.s_addr)){
		//		printf("Same subnet\n");
				strcpy(tempIP,Sock_ntop_host(sa,sizeof(*sa)));	
				i=1;
			}
			strcpy(clientIP,Sock_ntop_host(sa,sizeof(*sa))); 
		}
	
	}

	free_ifi_info_plus(ifihead);
	
	if(i==1)
		return tempIP;
	else
		return clientIP;
}

int main(int argc, char **argv) {

	char IPserver[INET_ADDRSTRLEN];
	char IPclient[INET_ADDRSTRLEN];
	char tempIP[INET_ADDRSTRLEN];
	struct sockaddr_in servaddr,cliaddr,peer;
	int sockfd, sockfd_s, len; //, sockfd_serv;
	int on = 1;	


    	printf("\nComputer Science 533: Assignment 2 \n");
    	printf("Group 16 - Keerthi Yendamuri, Raman Nanda, Supriya Vasudevan \n");
    	printf("Client Application \n\n");
    	struct clientArgumentsType clientArgs = clientArgumentProcessing();
    	printf("%s\n", clientArgs.serverIPAddress);
  
	/* Using the get_ifi_info_plus function */
	strcpy(tempIP,checkLocal(clientArgs));
	printf("checkLocal returns:%s\n",tempIP);
	printf("%s\n", clientArgs.serverIPAddress);
	if((strcmp(clientArgs.serverIPAddress,"127.0.0.1")) == 0){
		strcpy(IPserver,"127.0.0.1");
		strcpy(IPclient,"127.0.0.1");
		printf("IPserver:%s\n",IPserver);
		printf("IPclient:%s\n",IPclient);
		printf("IP N SERVER IN SAME MACHINE\n");
	}
	else{
		
		strcpy(IPserver,clientArgs.serverIPAddress);
		strcpy(IPclient,tempIP);
		printf("IPserver:%s\n",IPserver);
		printf("IPclient:%s\n",IPclient);
	}	
	//printf("IPserver:%s\n",IPserver);
	//printf("IPclient:%s\n",IPclient);

	/* Filling struct cliaddr */
	bzero(&cliaddr, sizeof(cliaddr));
	cliaddr.sin_family = AF_INET;
	cliaddr.sin_port = htons(0);
	if ((inet_pton(AF_INET,IPclient, &(cliaddr.sin_addr))) < 0) 
		printf("Error in inet_pton");

	/* Filling struct servaddr */
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	printf("PORT NO : %d\n",clientArgs.serverPortNumber);
	servaddr.sin_port = htons(clientArgs.serverPortNumber);
	if ((inet_pton(AF_INET,IPserver, &(servaddr.sin_addr))) < 0) 
		printf("Error in inet_pton");
	
	if ((sockfd = Socket(AF_INET, SOCK_DGRAM, 0)) < 0)
		printf("Error in socket\n");	
	
	printf("%d\n", sockfd);
	printf("%d\n", sizeof(cliaddr));
	printf("%s\n",inet_ntoa(cliaddr.sin_addr));


	if((sockfd_s = Socket(AF_INET, SOCK_DGRAM, 0)) < 0)
		printf("Error in socket\n");
		
	if((bind(sockfd, (SA*) &cliaddr,sizeof(cliaddr))) < 0)
		printf("Error in bind\n"); 


//	if((bind(sockfd_s, (SA*) &servaddr, sizeof(servaddr))) <0 )
//		printf("Error in bind\n");		

	len = sizeof(cliaddr);
	getsockname(sockfd, (SA*) &cliaddr, &len);
	printf("Client Address:%s\n",Sock_ntop((SA*) &cliaddr, len)); 
	printf("Client port:%d\n",cliaddr.sin_port);

	Connect(sockfd, (SA*) &servaddr, sizeof(servaddr));

	getpeername(sockfd, (SA*) &peer, &len);
	printf("%s\n",inet_ntoa(peer.sin_addr));
	printf("%u\n",(unsigned)ntohs(peer.sin_port)); 
	
	Setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));

	//Sendto(sockfd,clientArgs.fileName,sizeof(clientArgs.fileName),0,clientArgs.serverIPAddress,sizeof(clientArgs.serverIPAddress));
	printf("Reached before sendMessage\n");
	send_recv_Message(sockfd,clientArgs,(SA*)&servaddr,sizeof(servaddr));
	//receiveMessage(sockfd);
	 exit(0);
}


struct clientArgumentsType clientArgumentProcessing() {
    struct clientArgumentsType clientArgs;
    
    /** Segment For Reading Data From File **/
    FILE *argumentFile;
    char line[MAXLINE];
    int lineCount = 0;
    
    if(!(argumentFile = fopen (CLIENT_ARG_FILE, "rt"))) {
        printf("Error: Can not open file: %s", CLIENT_ARG_FILE);
        exit(0);
    }
    
    printf("Reading Arguments from File: %s\n", CLIENT_ARG_FILE);
    while(fgets(line, MAXLINE, argumentFile) != NULL) {
        lineCount++;
        //char straddr[INET_ADDRSTRLEN];
        //char strFileName[MAXLINE];
 //      	int i = 0; 
        /** To Remove New Line Return from Each Line **/
        size_t bufferlen = strlen(line);
        if (line[bufferlen - 1] == '\n') 
            line[bufferlen - 1] = '\0';
       
// 
       switch(lineCount) {
            case 1:
                clientArgs.serverIPAddress = malloc(sizeof(line));
                strncpy(clientArgs.serverIPAddress, line, sizeof(line));
                break;
            case 2:
                clientArgs.serverPortNumber = atoi(line);
                break;
            case 3:
                clientArgs.fileName = malloc(sizeof(line));
                strncpy(clientArgs.fileName, line, sizeof(line));
                break;
            case 4:
                clientArgs.recievingSlidingWindowSize = atoi(line);
                break;
            case 5:
                clientArgs.randomSeedValue = atoi(line);
                break;
            case 6:
                clientArgs.probabilityLoss = atof(line);
                break;
            case 7:
                clientArgs.mean = atol(line);
                break;
            default:
                printf("Error! Arguments Do NOT Match!");
                exit(0);
        }
    }
    fclose(argumentFile);
    return clientArgs;
}


ssize_t send_recv_Message(int fd,clientArgumentsType clientArgs,const SA* servaddr, socklen_t servlen){
	char fileName[50];	
	struct iovec iov_send[2], iov_recv[2];
	int n;	
	char sendline[MAXLINE],recvline[MAXLINE+1];
	int on=1;	
	char buf[MAXLINE];
	static struct rtt_info rttinfo;
	static int rttinit = 0;

 
	struct msghdr message_send, message_recv;
	struct hdr{
		uint32_t seq;
		uint32_t ts;
	}sendhdr,recvhdr;

	if(rttinit == 0){
		rtt_init(&rttinfo);
		rttinit = 1;
		rtt_d_flag = 1;

	}



	/* Sending Message */	
	sendhdr.seq++;
	iov_send[0].iov_base = &sendhdr;   
	iov_send[0].iov_len = sizeof(struct hdr); 
	iov_send[1].iov_base = clientArgs.fileName;
	iov_send[1].iov_len = sizeof(fileName);
	message_send.msg_name = NULL; //clientArgs.serverIPAddress;
	message_send.msg_namelen = 0;//clientArgs.serverIPAddress);
	message_send.msg_iov=iov_send;
	message_send.msg_iovlen=2;
	message_send.msg_accrights=0;
	message_send.msg_accrightslen=0;


	/* Receiving Message */
	iov_recv[0].iov_base = &recvhdr;   
	iov_recv[0].iov_len = sizeof(struct hdr); 
	iov_recv[1].iov_base = recvline;
	iov_recv[1].iov_len = MAXLINE;
	message_recv.msg_name = NULL; 
	message_recv.msg_namelen = 0;
	message_recv.msg_iov=iov_recv;
	message_recv.msg_iovlen=2;
	message_recv.msg_accrights=0;
	message_recv.msg_accrightslen=0;
	


	/* Printing the information to be sent */
	printf("FileName:%s\n",clientArgs.fileName);
	printf("In sendMessage\n");
	printf("ServerAddress in sendMesage:%s\n",clientArgs.serverIPAddress);
	printf("Sequence Number Sent:%4d\n",sendhdr.seq);  

	Signal(SIGALRM, sig_alrm);
	rtt_newpack(&rttinfo);

sendagain:
#ifdef RTT_DEBUG
	fprintf(stderr,"send %4d:\n",sendhdr.seq);
#endif
	sendhdr.ts = rtt_ts(&rttinfo);
//	Setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	printf("Before sendmsg\n");
	//Sendmsg(fd,&message_send,0);   
	Sendto(fd,clientArgs.fileName,strlen(clientArgs.fileName),MSG_DONTROUTE,NULL,0);

	alarm(rtt_start(&rttinfo));
#ifdef RTT_DEBUG
	rtt_debug(&rttinfo);
#endif

	if(sigsetjmp(jmpbuf,1) != 0){
		if(rtt_timeout(&rttinfo) < 0){
			err_msg("client: no response from server, giving up");
			rttinit = 0;
			errno = ETIMEDOUT;
			return(-1);
		}
#ifdef RTT_DEBUG
		err_msg("client: timeout, retransmitting");
#endif
		goto sendagain;
	}


	printf("Waiting for server to respond\n");

	bzero(buf,MAXLINE);
	
	do{
		fprintf(stdout, "Checking\n");
		//n =Recvmsg(fd,&message_recv,0);  
		n = Recvfrom(fd,recvline,MAXLINE,MSG_DONTROUTE,NULL, NULL);	
		printf("%s\n", recvline);		
		if((strcmp(recvline,clientArgs.fileName))==0){
			printf("Received the sent file name: Ack Recd\n");
			Sendto(fd,"Ack",strlen("Ack"),MSG_DONTROUTE,NULL,0);	

		}
		else
			printf("Ack not received\n");
		fprintf(stderr, "recv %4d\n",recvhdr.seq);
	}while(1); //recvhdr.seq != sendhdr.seq);	



	
	alarm(0);
	rtt_stop(&rttinfo, rtt_ts(&rttinfo) - recvhdr.ts);
	return(n-sizeof(struct hdr));	

}

// Pass the file descriptor to receive message on that descriptor.
void receiveMessage(int fd) {
	struct msghdr message;
	//int flags = MSG_DONTROUTE;
  int idx = 0;
	
	// Error check.
	if (recvmsg(fd, &message, MSG_DONTROUTE) == -1) {
		printf("Error receiving message\n");
	}
	
	// There will be message.iovlen number of msg_iov.
	for (idx = 0; idx < message.msg_iovlen; ++idx) {
		struct iovec a_iovec = message.msg_iov[idx];
		printf("Message : %s\n", a_iovec.iov_base);
	}
	
	return;
}

/*
void sendMessage(int fd,clientArgumentsType clientArgs){
	struct iovec iov[1];
	strcpy(iov[0].iov_base,clientArgs.fileName);
	iov[0].iov_len = sizeof(clientArgs.fileName);

	struct msghdr message;
	message.msg_name = clientArgs.serverIPAddress;
	message.msg_namelen = sizeof(clientArgs.serverIPAddress);
	message.msg_iov=iov;
	message.msg_iovlen=1;
	message.msg_accrights=0;
	message.msg_accrightslen=0;

	if((sendmsg(fd,&message,0))== -1){
		perror("Sendmsg error");

	}	

}*/

